﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OpenTK;
using OpenTK.Graphics;
using OpenTK.Graphics.OpenGL;
using System.Runtime.CompilerServices;
using System.IO;
using System.Drawing;
[assembly: InternalsVisibleTo ("MyTestProject")]


/**
 * BindTarget(Object target)の引数はObject型でなく明示的に
 * Graphics(相当のPSSのオブジェクト),Image2D,RenderTargetにしようか?
 * */
/* プロパティの値は全て仕様書の要求する最低条件
 * 実際のHWを反映していないことに注意。
 */

namespace M3G {
    public static class OpenTKExtensions {
        public static ShaderVariableType ToM3G (this OpenTK.Graphics.OpenGL.ActiveAttribType type) {
            switch (type) {
                case ActiveAttribType.Float: return ShaderVariableType.Float;
                case ActiveAttribType.FloatVec2: return ShaderVariableType.Vec2;
                case ActiveAttribType.FloatVec3: return ShaderVariableType.Vec3;
                case ActiveAttribType.FloatVec4: return ShaderVariableType.Vec4;
                case ActiveAttribType.FloatMat2: return ShaderVariableType.Mat2;
                case ActiveAttribType.FloatMat3: return ShaderVariableType.Mat3;
                case ActiveAttribType.FloatMat4: return ShaderVariableType.Mat4;
                default: return 0;
            }
        }
        public static ShaderVariableType ToM3G (this OpenTK.Graphics.OpenGL.ActiveUniformType type) {
            switch (type) {
                case ActiveUniformType.Float: return ShaderVariableType.Float;
                case ActiveUniformType.FloatVec2: return ShaderVariableType.Vec2;
                case ActiveUniformType.FloatVec3: return ShaderVariableType.Vec3;
                case ActiveUniformType.FloatVec4: return ShaderVariableType.Vec4;
                case ActiveUniformType.Int: return ShaderVariableType.Int;
                case ActiveUniformType.IntVec2: return ShaderVariableType.IVec2;
                case ActiveUniformType.IntVec3: return ShaderVariableType.IVec3;
                case ActiveUniformType.IntVec4: return ShaderVariableType.IVec4;
                case ActiveUniformType.Bool: return ShaderVariableType.Bool;
                case ActiveUniformType.BoolVec2: return ShaderVariableType.BVec2;
                case ActiveUniformType.BoolVec3: return ShaderVariableType.BVec3;
                case ActiveUniformType.BoolVec4: return ShaderVariableType.BVec4;
                case ActiveUniformType.FloatMat2: return ShaderVariableType.Mat2;
                case ActiveUniformType.FloatMat3: return ShaderVariableType.Mat3;
                case ActiveUniformType.FloatMat3x4: return ShaderVariableType.Mat3x4;
                case ActiveUniformType.FloatMat4: return ShaderVariableType.Mat4;
                case ActiveUniformType.Sampler2D: return ShaderVariableType.Sampler2D;
                case ActiveUniformType.SamplerCube: return ShaderVariableType.SamplerCube;
                default: return 0;
            }
        }
        public static OpenTK.Graphics.OpenGL.VertexAttribPointerType ToGL (this M3G.ArrayType type) {
            switch (type) {
                case ArrayType.Byte: return VertexAttribPointerType.UnsignedByte;
                case ArrayType.Fixed: return VertexAttribPointerType.UnsignedShort; // これであってる?
                case ArrayType.Float: return VertexAttribPointerType.Float;
                case ArrayType.Half: return VertexAttribPointerType.HalfFloat;
                case ArrayType.Short: return VertexAttribPointerType.UnsignedShort;
                default: return 0;
            }
        }

        public static OpenTK.Graphics.OpenGL.BeginMode ToGL (this M3G.PrimitiveType type) {
            switch (type) {
                case PrimitiveType.Lines: return BeginMode.Lines;
                case PrimitiveType.Triangles: return BeginMode.Triangles;
                case PrimitiveType.PointSprites: return BeginMode.Points;
                default: return 0;
            }
        }

        public static void Uniform2 (this OpenTK.Graphics.OpenGL.GL gl, int location, bool v0, bool v1) {

        }

        public static bool LoadValue (this M3G.ShaderVariable shUnif, object[] values, Dictionary<Object3D, int> glResources) {
            var loc = shUnif.Location;
            var arraySize = shUnif.ArraySize;
            if (arraySize == 0) {
                arraySize = 1;
            }
            var length = arraySize * shUnif.Type.ToElementCount ();

                switch (shUnif.Type) {
                    case ShaderVariableType.Float: GL.Uniform1 (loc, arraySize, values.Cast<float>().ToArray()); break;
                    case ShaderVariableType.Bool: GL.Uniform1 (loc, arraySize, values.Cast<bool> ().Select(x => (x == true) ? 1 : 0).ToArray()); break;
                    case ShaderVariableType.Int: GL.Uniform1 (loc, arraySize, values.Cast<int> ().ToArray ()); break;

                    case ShaderVariableType.Vec2: GL.Uniform2 (loc, arraySize, values.Cast<float>().ToArray()); break;
                    case ShaderVariableType.Vec3: GL.Uniform3 (loc, arraySize, values.Cast<float>().ToArray()); break;
                    case ShaderVariableType.Vec4: GL.Uniform4 (loc, arraySize, values.Cast<float> ().ToArray ()); break;

                    case ShaderVariableType.IVec2: GL.Uniform2 (loc, arraySize, values.Cast<int>().ToArray()); break;
                    case ShaderVariableType.IVec3: GL.Uniform3 (loc, arraySize, values.Cast<int>().ToArray()); break;
                    case ShaderVariableType.IVec4: GL.Uniform4 (loc, arraySize, values.Cast<int> ().ToArray ()); break;

                    case ShaderVariableType.BVec2: GL.Uniform2 (loc, arraySize, values.Cast<bool> ().Select (x => (x == true) ? 1 : 0).ToArray ()); break;
                    case ShaderVariableType.BVec3: GL.Uniform3 (loc, arraySize, values.Cast<bool> ().Select (x => (x == true) ? 1 : 0).ToArray ()); break;
                    case ShaderVariableType.BVec4: GL.Uniform4 (loc, arraySize, values.Cast<bool> ().Select (x => (x == true) ? 1 : 0).ToArray ()); break;

                    // 現在はMat4のスカラーだけ対応。
                    // Flattenがないから...
                    case ShaderVariableType.Mat2: GL.UniformMatrix2 (loc, 1, true, ((Transform)values[0]).Matrix); break;
                    case ShaderVariableType.Mat4: GL.UniformMatrix4 (loc, 1, true, ((Transform)values[0]).Matrix); break;
                    case ShaderVariableType.Mat3x4: GL.UniformMatrix4x3 (loc, 1, true, ((Transform)values[0]).Matrix); break;

                    // テクスチャーユニット番号の扱いについては未定
                    case ShaderVariableType.Sampler2D: GL.Uniform1 (loc, arraySize, values.Cast<Texture> ().Select (x => glResources[x]).ToArray ()); break;
                    case ShaderVariableType.SamplerCube: GL.Uniform1 (loc, arraySize, values.Cast<Texture>().Select(x => glResources[x]).ToArray ()); break;
                    default: break;
                }
       

            return true;
        }

    }

    public class Graphics3D {
        #region Constant
        public const int	Antialias   = 2;
        public const int	Dither      = 4;
        public const int	TrueColor   = 8;
        public const int	Overwite    = 16;
        public const int	NoOverlays  = 32;
        public const int	Pure3D      = 48;   // ?
        public const int	Depth       = 64;
        public const int	Stencil	    = 128;
        public const int	Validate    = 256;
        #endregion

        #region Field
        static Graphics3D g3d;
        Object target;
        int    renderingFlags;
        float  depthRangeNear;
        float  depthRangeFar;
        float  viewportX;
        float  viewportY;
        float  viewportWidth;
        float  viewportHeight;

        Dictionary<Object3D, int> glResources;
        #endregion

        #region Method
        Graphics3D () {
            target = null;
            renderingFlags = 0;
            depthRangeNear = 0.0f;
            depthRangeFar = 1.0f;
            viewportX = 0;
            viewportY = 0;
            viewportWidth = 0;
            viewportHeight = 0;

            glResources = new Dictionary<Object3D, int> ();
        }


        public static Graphics3D GetInstance () {
            if (g3d == null) {
                g3d = new Graphics3D ();
            }
            return g3d;
        }

        public Dictionary<Object3D, int> GlCache {
            get { return glResources; }
        }

        public float DepthRangeNear {
            get { return depthRangeNear; }
        }

        public float DepthRangeFar {
            get { return depthRangeFar; }
        }

        public void SetDepthRange (float near, float far) {
            if (near < 0 || near > 1 || far < 0 || far > 1) {
                throw new ArgumentException ("Depth ranges must be within [0,1], near=" + near + ",far=" + far);
            }

            this.depthRangeNear = near;
            this.depthRangeFar = far;
        }

        public static Dictionary<String, Object> GetProperties (Object target) {
            // 今だけデフォルト値
            return GetProperties ();
        }

        // 本当はレンダリングターゲットごとにまじめに作るべきだが
        // 今は"Baseline for Advanced Implementations"をそのまま返している
        public static Dictionary<String, Object> GetProperties () {
            Dictionary<String,Object> properties;
            properties = new Dictionary<string, object> ();
            properties.Add ("supportAntialiasing", false);
            properties.Add ("supportTrueColor", false);
            properties.Add ("supportDithering", false);
            properties.Add ("supportMipmapping", true);
            properties.Add ("supportNativeTextureNPOT", true);
            properties.Add ("supportNativeMipmapNPOT", false);
            properties.Add ("supportNatvieETC", false);
            properties.Add ("supportNativePalette", false);
            properties.Add ("supportPerspectiveCorrection", true);
            properties.Add ("supportLocalCameraLighting", false);
            properties.Add ("maxLights", 8);
            properties.Add ("maxViewportWidth", 1024);
            properties.Add ("maxViewportHeight", 1024);
            properties.Add ("maxViewportDimension", 1024);
            properties.Add ("maxTextureDimension", 1024);
            properties.Add ("maxTextureCubeDimension", 1024);
            properties.Add ("maxSprteCropDimension", 1024);
            properties.Add ("maxTransformsPerVertex", 4);
            properties.Add ("maxLineWidth", 1.0f);
            properties.Add ("maxPointSize", 1.0f);
            properties.Add ("maxVertexAttributes", 8);
            properties.Add ("maxVertexAttributesFixedFunction", 1);
            properties.Add ("maxVertexUniforms", 128);
            properties.Add ("maxVertexUniformsFixedFunction", 16);
            properties.Add ("maxFragmentUniforms", 16);
            properties.Add ("maxVaryings", 8);
            properties.Add ("numTextureUnits", 2);
            properties.Add ("numFragmentSamplers", 8);
            properties.Add ("numVertexSamplers", 0);
            properties.Add ("numCombinedSamplers", 8);
            properties.Add ("numStencilBufferBits", 8);
            properties.Add ("dynamicSouces", null);
            properties.Add ("GLExtensions", null);
            properties.Add ("Renderer", "Unknown renderer");
            properties.Add ("Vendor", "UEDA Takashi");
            properties.Add ("ShadingLanguageVersion", "OpenGL ES GLSL ES Unknown");

            return properties;
        }

        public int RenderingFlags {
            get { return renderingFlags; }
        }


        public float ViewportX {
            get { return viewportX; }
        }

        public float ViewportY {
            get { return viewportY; }
        }

        public float ViewportWidth {
            get { return viewportWidth; }
        }

        public float ViewportHeight {
            get { return viewportHeight; }
        }

        public void SetViewport (int x, int y, int width, int height) {
            var props = GetProperties ();
            if (width < 0 || width > (int)props["maxViewportWidth"] || y < 0 || height >= (int)props["maxViewportHeight"]) {
                throw new ArgumentException ("Viewport size is invalid, w=" + width + ",h=" + height);
            }

            this.viewportX = x;
            this.viewportY = y;
            this.viewportWidth = width;
            this.viewportHeight = height;
        }


        public void BindTarget (Object target) {
            BindTarget (target, Depth);
        }

        public void BindTarget (Object target, int flags) {
            //if (target == null) {
            //    throw new ArgumentNullException ("Target is null");
            //}
            if (this.target != null) {
                throw new InvalidOperationException ("This Graphics3D already has a rendering target");
            }

            this.target = target;
            this.renderingFlags = flags;
        }

        public void ReleaseTarget () {
            this.target = null;
        }

        public Object Target {
            get { return target; }
        }

        public void Render (RenderPass renderPass) {
            throw new NotImplementedException ("Sorry");
        }

        public void Render (World world) {
            //if (world == null) {
            //    throw new ArgumentNullException ("World is null");
            //}
            //if (target == null) {
            //   throw new InvalidOperationException ("Rendering target is null");
            //}
            //if (world.ActiveCamera == null) {
            //    throw new InvalidOperationException ("Active camera does not exist");
            //}
            // ほかいくつか

            // 未実装
            var objs = world.FindAll<Object3D> ();

            Render (objs.Where (x => x as Background != null).Cast<Background> ());
            Render (objs.Where (x => x as VertexShader != null).Cast<VertexShader> ());
            Render (objs.Where (x => x as FragmentShader != null).Cast<FragmentShader> ());
            Render (objs.Where (x => x as ShaderProgram != null).Cast<ShaderProgram> ());
            Render (objs.Where (x => x as ShaderAppearance != null).Cast<ShaderAppearance> ());
            Render (objs.Where (x => x as VertexArray != null).Cast<VertexArray> ());
            Render (objs.Where (x => x as VertexBuffer != null).Cast<VertexBuffer> ());
            Render (objs.Where (x => x as IndexBuffer != null).Cast<IndexBuffer> ());
            Render (objs.Where (x => x as ShaderUniforms != null).Cast<ShaderUniforms> ());
            Render (objs.Where (x => x as Mesh != null).Cast<Mesh> ());
        }

        internal void Render (IEnumerable<Background> objs) {
            foreach (var bg in objs) {
                var maskA = ((bg.ColorClearMask & 0xff000000) > 0) ? true : false;
                var maskR = ((bg.ColorClearMask & 0x00ff0000) > 0) ? true : false;
                var maskG = ((bg.ColorClearMask & 0x0000ff00) > 0) ? true : false;
                var maskB = ((bg.ColorClearMask & 0x000000ff) > 0) ? true : false;
                GL.ColorMask (maskR, maskG, maskB, maskA);

                var colA = ((bg.Color & 0xff000000) >> 24) / 255.0f;
                var colR = ((bg.Color & 0x00ff0000) >> 16) / 255.0f;
                var colG = ((bg.Color & 0x0000ff00) >> 8) / 255.0f;
                var colB = ((bg.Color & 0x000000ff) >> 0) / 255.0f;
                GL.ClearColor (colR, colG, colB, colA);

                var depth = bg.Depth;
                GL.ClearDepth (depth);

                var clearBits = ClearBufferMask.ColorBufferBit;
                if (bg.DepthClearEnabled) {
                    clearBits |= ClearBufferMask.DepthBufferBit;
                }
                GL.Clear (clearBits);

                GL.ColorMask (true, true, true, true);
            }

        }

        internal void Render (IEnumerable<FragmentShader> objs) {
            foreach (var fragShader in objs) {
                if (glResources.ContainsKey (fragShader)) {
                    continue;
                }

                string log;
                int    result;

                var id = GL.CreateShader (ShaderType.FragmentShader);
                if (id == 0) {
                    throw new ShaderException ("Creating FragmentShader was failed.");
                }
                GL.ShaderSource (id, fragShader.Source);
                GL.CompileShader (id);
                GL.GetShaderInfoLog (id, out log);

                fragShader.SetInfoLog (log);
                AddGLResource (fragShader, id);

                GL.GetShader (id, ShaderParameter.CompileStatus, out result);
                if (result == 0) {
                    throw new ShaderException ("Compiling FragmentShader was failed, " + log);
                }

            }
        }

        internal void Render (IEnumerable<VertexBuffer> objs) {
            // Do nothing
        }

        internal void Render (IEnumerable<ShaderAppearance> objs) {
            // Do nothing
        }

        internal void Render (IEnumerable<ShaderUniforms> objs) {
            foreach (var unifs in objs) {
                unifs.Refresh ();
            }
        }

        internal void Render (IEnumerable<VertexShader> objs) {
            foreach (var vertShader in objs) {
                if (glResources.ContainsKey (vertShader)) {
                    continue;
                }

                string log;
                int    result;

                var id = GL.CreateShader (ShaderType.VertexShader);
                if (id == 0) {
                    throw new ShaderException ("Creating VertexShader was failed.");
                }
                GL.ShaderSource (id, vertShader.Source);
                GL.CompileShader (id);
                GL.GetShaderInfoLog (id, out log);

                vertShader.SetInfoLog (log);
                AddGLResource (vertShader, id);

                GL.GetShader (id, ShaderParameter.CompileStatus, out result);
                if (result == 0) {
                    throw new ShaderException ("Compiling VertexShader was failed, " + log);
                }

            }
        }


        /**
         * ShaderProgramのレンダリング
         * (メモ)Attribute変数のロケーションを保存していない
         * ShaderVariableはObject3Dを継承していないからDictionary<Object3D, int>に突っ込めない!
         * どうするかは検討中
         *   (1) もう1つ別に用意する
         *   (2) Dictionary<object, int>に変更する(DisposeEventをセットできなくなるから多分無理)
         * */
        internal void Render (IEnumerable<ShaderProgram> objs) {
            foreach (var shaderProg in objs) {
                if (glResources.ContainsKey (shaderProg)) {
                    continue;
                }

                string log;
                int result;

                var id = GL.CreateProgram ();
                if (id == 0) {
                    throw new ShaderException ("Creating ShaderProgram was failed");
                }

                GL.AttachShader (id, glResources[shaderProg.VertexShader]);
                GL.AttachShader (id, glResources[shaderProg.FragmentShader]);
                GL.LinkProgram (id);
                GL.GetProgramInfoLog (id, out log);

                shaderProg.SetInfoLog (log);
                AddGLResource (shaderProg, id);

                GL.GetProgram (id, ProgramParameter.LinkStatus, out result);
                if (result == 0) {
                    throw new ShaderException ("Linking was failed, " + log);
                }

                // ShaderVariable(Attributes)の作成
                int attributeCount;
                GL.GetProgram (id, ProgramParameter.ActiveAttributes, out attributeCount);
                var attrs = new ShaderVariable[attributeCount];
                for (var i=0; i < attributeCount; i++) {
                    ActiveAttribType type;
                    int size;
                    var name = GL.GetActiveAttrib (id, i, out size, out type);
                    var loc  = GL.GetAttribLocation (id, name);
                    attrs[i] = new ShaderVariable (name, type.ToM3G (), size, loc);
                }
                shaderProg.SetAttributes (attrs);

                // ShaderVariable(Uniforms)の作成
                int uniformCount;
                GL.GetProgram (id, ProgramParameter.ActiveUniforms, out uniformCount);
                var unifs = new ShaderVariable[uniformCount];
                for (var i=0; i < uniformCount; i++) {
                    ActiveUniformType type;
                    int size;
                    var name = GL.GetActiveUniform (id, i, out size, out type);
                    var loc  = GL.GetUniformLocation (id, name);

                    unifs[i] = new ShaderVariable (name, type.ToM3G (), size, loc);
                }

                shaderProg.SetUniforms (unifs);
            }
        }

        /**
         * VertexArrayのレンダリング
         * (メモ)使用してないデータも問答無用でGPUに転送するけど良いのか?
         * */
        internal void Render (IEnumerable<VertexArray> objs) {
            foreach (var varry in objs) {
                if (glResources.ContainsKey (varry)) {
                    continue;
                }

                int id;
                GL.GenBuffers (1, out id);
                if (id == 0) {
                    throw new ShaderException ("Creating ArrayBuffer was failed.");
                }

                GL.BindBuffer (BufferTarget.ArrayBuffer, id);
                GL.BufferData (BufferTarget.ArrayBuffer, new IntPtr (varry.ByteArray.Length), varry.ByteArray, BufferUsageHint.StaticDraw);

                GL.BindBuffer (BufferTarget.ArrayBuffer, 0);
                AddGLResource (varry, id);
            }
        }

        /**
         * IndexBufferのレンダリング
         * */
        internal void Render (IEnumerable<IndexBuffer> objs) {
            foreach (var ibuf in objs) {
                if (glResources.ContainsKey (ibuf)) {
                    continue;
                }

                int id;
                GL.GenBuffers (1, out id);
                if (id == 0) {
                    throw new ShaderException ("Creating ElementArrayBuffer was failed.");
                }

                GL.BindBuffer (BufferTarget.ElementArrayBuffer, id);
                GL.BufferData (BufferTarget.ElementArrayBuffer, new IntPtr (ibuf.IndexCount * 4), ibuf.ByteArray (), BufferUsageHint.StaticDraw);

                GL.BindBuffer (BufferTarget.ElementArrayBuffer, 0);
                AddGLResource (ibuf, id);
            }
        }


        /**
         * Meshのレンダリング
         * void VertexAttribPointer (int index, int compSize, VertexAttribPointerType compType, bool normalized, int stride, int offset);
         * void DrawElements (BeginMode mode, int count, DrawElementsType type, int offset);
         * VAOはShaderAppearanceに紐づけられている。
         * */
        internal void Render (IEnumerable<Mesh> objs) {
            foreach (var msh in objs) {
                for (var i=0; i < msh.SubmeshCount; i++) {
                    var vbuf       = msh.VertexBuffer;
                    var ibuf       = msh.IndexBuffer[i];
                    var app        = msh.ShaderAppearance[i];
                    var unifsArray = msh.ShaderAppearance[i].ShaderUniforms;
                    var shProg     = msh.ShaderAppearance[i].ShaderProgram;
                    var shUnifs    = msh.ShaderAppearance[i].ShaderProgram.Uniforms;
                    var shAttrs    = msh.ShaderAppearance[i].ShaderProgram.Attributes;

                    if (!glResources.ContainsKey (app)) {
                        int id;
                        GL.GenVertexArrays (1, out id);
                        GL.BindVertexArray (id);

                        var pos = shAttrs.FirstOrDefault (x => x.Name == "m3g_Position");
                        if (pos != null) {
                            var scaleBias = new float[5];
                            var positions = msh.VertexBuffer.Positions (scaleBias);  // Scale, Biasの値はどうやってGLSL側に反映する?
                            var compSize  = positions.ComponentCount;
                            var compType  = positions.ComponentType.ToGL ();
                            GL.BindBuffer (BufferTarget.ArrayBuffer, glResources[positions]);
                            GL.EnableVertexAttribArray (pos.Location);
                            GL.VertexAttribPointer (pos.Location, compSize, compType, false, 0, 0);
                        }


                        var norm = shAttrs.FirstOrDefault (x => x.Name == "m3g_Normal");
                        if (norm != null) {
                            var normals  = msh.VertexBuffer.Normals (null);
                            var compSize = normals.ComponentCount;
                            var compType = normals.ComponentType.ToGL ();
                            GL.BindBuffer (BufferTarget.ArrayBuffer, glResources[normals]);
                            GL.EnableVertexAttribArray (norm.Location);
                            GL.VertexAttribPointer (norm.Location, compSize, compType, true, 0, 0);
                        }

                        var col = shAttrs.FirstOrDefault (x => x.Name == "m3g_Color");
                        if (col != null) {
                            var colors   = msh.VertexBuffer.Colors (null);
                            var compSize = colors.ComponentCount;
                            var compType = colors.ComponentType.ToGL ();
                            GL.BindBuffer (BufferTarget.ArrayBuffer, glResources[colors]);
                            GL.EnableVertexAttribArray (col.Location);
                            GL.VertexAttribPointer (col.Location, compSize, compType, true, 0, 0);
                        }

                        GL.BindBuffer (BufferTarget.ElementArrayBuffer, glResources[ibuf]);
                        AddGLResource (app, id);
                    }

                    // 準備OK!, レンダリング


                    // シェーダーのセット
                    GL.UseProgram (glResources[shProg]);

                    // Uniform変数のセット
                    // (固定機能とユーザー定義の両方)
                    foreach (var shUnif in shUnifs) {
                        foreach (var unifs in unifsArray) {
                            var unif = unifs.AllShaderVariables.FirstOrDefault (x => x.Name == shUnif.Name);
                            if (unif != null) {
                                shUnif.LoadValue (unif.Values, glResources);
                            }
                        }
                    }


                    // 描画コマンドの発行
                    GL.BindVertexArray (glResources[app]);
                    GL.DrawElements (ibuf.PrimitiveType.ToGL (), ibuf.IndexCount, DrawElementsType.UnsignedInt, IntPtr.Zero);

                    GL.BindVertexArray (0);
                }
            }

        }

        void SendShaderUniformValue (ShaderVariable shUnifVar, Object value) {
        }



        void DisposeEventHandler (object sender, EventArgs e) {
            var obj = (Object3D)sender;

            RemoveGLResource (obj);
        }

        /**
         * GLリソースの登録追加
         * */
        void AddGLResource (Object3D obj, int value) {
            glResources.Add (obj, value);

            obj.DisposeEvent += new DisposeEventHandler (DisposeEventHandler);
        }

        /**
         * GLリソースの登録削除
         * */
        void RemoveGLResource (Object3D obj) {

            // GLのリソースの解放
            // オブジェクトの型に応じた処理
            if ((obj as FragmentShader) != null) {
                GL.DeleteShader (glResources[obj]);
            }
            if ((obj as VertexShader) != null) {
                GL.DeleteShader (glResources[obj]);
            }
            if ((obj as ShaderProgram) != null) {
                GL.DeleteProgram (glResources[obj]);
            }
            if ((obj as VertexArray) != null) {
                GL.DeleteBuffers (1, new[] { glResources[obj] });
            }
            if ((obj as IndexBuffer) != null) {
                GL.DeleteBuffers (1, new[] { glResources[obj] });
            }
            if ((obj as ShaderAppearance) != null) {
                GL.DeleteVertexArrays (1, new[] { glResources[obj] });
            }

            glResources.Remove (obj);
            obj.DisposeEvent -= new DisposeEventHandler (DisposeEventHandler);
        }

        public void SetContext (IGraphicsContext context) {

        }

        public Bitmap CaptureScreen () {
            var x = (int)viewportX;
            var y = (int)viewportY;
            var width = (int)viewportWidth;
            var height = (int)viewportHeight;
            var bmp = new Bitmap (width, height);
            var data = bmp.LockBits (new Rectangle (x, y, width, height),
                                       System.Drawing.Imaging.ImageLockMode.WriteOnly, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
            GL.ReadPixels (x, y, width, height, PixelFormat.Bgr, PixelType.UnsignedByte, data.Scan0);
            bmp.UnlockBits (data);
            bmp.RotateFlip (RotateFlipType.RotateNoneFlipY);
            return bmp;
        }

        #endregion


    }
}
